using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;
using UmlDesigner.Gui;
using UmlDesigner.Model.WorkUnits;

namespace UmlDesigner.Model
{
    [Serializable]
    class ClassWidget : ItemWidget
    {
        internal override bool IsPointWithin(Point p)
        {
            return ClassRect.Contains(p);
        }

        internal override void PaintSelectionOutline(Graphics e)
        {
            Pen stippledPen = new Pen(Color.Black);
            DrawSelection(e, stippledPen);
        }

        private void DrawSelection(Graphics e, Pen stippledPen)
        {
            Rectangle selClassRect = ClassRect;
            selClassRect.Width += 8;
            selClassRect.Height += 8;
            selClassRect.X -= 4;
            selClassRect.Y -= 4;
            pattern[0] = 5.0f;
            pattern[1] = 5.0f;
            stippledPen.DashPattern = pattern;
            e.DrawRectangle(stippledPen, selClassRect);
        }

        public override void HandleFocusedOutline(Graphics g)
        {
            Pen stippledPen = new Pen(Color.BlueViolet, 2);
            DrawSelection(g, stippledPen);
        }

        public override void OnSetFocus()
        {
            
        }

        public override void OnKiilFocus()
        {
            
        }

        internal override void PaintItem(Graphics e)
        {
            if (!Properties.Settings.Default.FixedWidthClassWidgets)
            {
                int width = ItemSize.Width;
                int height = ItemSize.Height;

                MeasureDisplayString(e, ref width, name);

                int numLines = methods.Count + attributes.Count;

                // Need to take into account the width of the largest method/attribute and the number of them.
                if (Properties.Settings.Default.ShowAttrsAndMethods)
                {
                    if (Properties.Settings.Default.ShowAttrsAndMethods)
                    {
                        foreach (UmlMethod m in methods)
                        {
                            MeasureDisplayString(e, ref width, m.GetAsDisplayableString());
                        }

                        foreach (UmlDesigner.Model.Attribute a in attributes)
                        {
                            MeasureDisplayString(e, ref width, a.GetAsDisplayableString());
                        }
                    }
                }

                height = Math.Max(height, 16 * numLines + 70);
//                height = Math.Max(height, (int)e.MeasureString(name, SystemFonts.DefaultFont).Height * numLines);
                ItemSize = new Size(width, height);
            }
            else
            {
                // TODO: need to cut down excessively long strings, and center the result.
            }

            Point point = Position;
            Rectangle classRect = ClassRect;
            Rectangle classBodyRect = classRect;
            classBodyRect.Y += 30;
            classBodyRect.Height = ClassRect.Height - 30;

            Color outlineColour = classOutlineColor;
            Pen outlinePen = new Pen(outlineColour);
            SolidBrush topFill = new SolidBrush(classBackgroundColor);
            LinearGradientBrush classBkgd = new LinearGradientBrush(classBodyRect, classBackgroundColor, Color.White, 45.0f);

            e.FillRectangle(topFill, classRect);
            e.DrawRectangle(outlinePen, classRect);

            e.FillRectangle(classBkgd, classBodyRect);
            e.DrawRectangle(outlinePen, classBodyRect);

            int headingX = (classRect.Width / 2) - ((int)e.MeasureString(name, SystemFonts.DefaultFont).Width / 2);

            Font font = (Properties.Settings.Default.UseCustomFont) ? Properties.Settings.Default.CustomFont : SystemFonts.DefaultFont;
            e.DrawString(name, font, new SolidBrush(Color.Black), new Point(Position.X + headingX, Position.Y + 10));

            if (Properties.Settings.Default.ShowAttrsAndMethods)
            {
                const int spacer = 10;
                int currY = classBodyRect.Y + spacer;

                foreach (UmlMethod m in methods)
                {
                    e.DrawString(m.GetAsDisplayableString(), font, new SolidBrush(Color.Black), new Point(Position.X + 10, currY));
                    currY += 16;
                }

                currY += spacer;
                e.DrawLine(outlinePen, Position.X, currY, classBodyRect.Right, currY);
                currY += spacer;

                foreach (UmlDesigner.Model.Attribute a in attributes)
                {
                    e.DrawString(a.GetAsDisplayableString(), font, new SolidBrush(Color.Black), new Point(Position.X + 10, currY));
                    currY += 16;
                }

            }

        }

        private void MeasureDisplayString(Graphics e, ref int width, string s)
        {
            Size headingSize = new Size((int)e.MeasureString(s, SystemFonts.DefaultFont).Width + 20, ItemSize.Height);
            width = Math.Max(width, headingSize.Width);
        }

        public override void HandleDoubleClicked(UmlControl ctrl)
        {
            ClassWidget widget = new ClassWidget();
            ShallowCopyClassWidget(widget);
            ClassProperties props = new ClassProperties(widget);
            if (props.ShowDialog() == DialogResult.OK)
            {
                // Add the changed state as a work unit.
                ChangeClassStateWorkUnit unit = new ChangeClassStateWorkUnit(this, widget);
                ctrl.MainForm.AddWorkUnit(unit);
            }
        }

        internal override bool ItemWithinArea(Rectangle rect)
        {
            return ClassRect.IntersectsWith(rect);
        }

        internal override bool IsClass()
        {
            return true;
        }

        internal override void GenerateCode(CodeGenerationSettings settings)
        {
            // Validate the class name is valid.
            if (name.IndexOfAny("/\\*:<>".ToCharArray()) != -1)
            {
                throw new CodeGenerationException("Error: Invalid class name \"" + name + "\". Class names containing '/', '\\', '*', '<', '>', or ':' cannot be generated as code.");
            }

            string newFileName = settings.OutputDirectory + "\\" + name + ".cs";
            settings.ProcessedFiles.Add(newFileName);
            StreamWriter writer = new StreamWriter(newFileName, false, Encoding.ASCII);
            settings.StartNewItem(writer, 0);
            GenerateCodeStub(settings);
            writer.Close();
        }

        internal void GenerateCodeStub(CodeGenerationSettings settings)
        {
            settings.WriteIndentedLine("using System;");
            settings.WriteIndentedLine("using System.Collections.Generic;");
            settings.WriteIndentedLine("using System.Text;");
            settings.WriteIndentedLine("");
            settings.WriteIndentedLine("namespace " + settings.DefaultNamespace);
            settings.WriteIndentedLine("{");

            settings.IncreaseIndent();
            foreach (LinkWidget link in links)
            {
                if (link.GetDestination().IsNote())
                {
                    link.GenerateCode(settings);
                }
            }

            settings.WriteIndentedLine("class " + name + GetBaseClasses());
            settings.WriteIndentedLine("{");
            settings.IncreaseIndent();

            foreach (LinkWidget link in links)
            {
                if (link.GetDestination().IsClass() && link.LinkType == LinkWidget.LineType.Association)
                {
                    Debug.WriteLine("Codifying link: " + link.GetHashCode().ToString());
                    link.GenerateCode(settings);
                    settings.WriteIndentedLine("");
                }
            }

            foreach (UmlMethod m in methods)
            {
                m.GenerateCode(settings);
            }

            foreach (UmlDesigner.Model.Attribute a in attributes)
            {
                a.GenerateCode(settings);
            }

            settings.DecreaseIndent();
            settings.WriteIndentedLine("}");

            settings.DecreaseIndent();
            settings.WriteIndentedLine("}");
        }

        private string GetBaseClasses()
        {
            StringBuilder s = new StringBuilder();;

            foreach (LinkWidget link in links)
            {
                if (link.LinkType == LinkWidget.LineType.Inheritance)
                {
                    if (s.Length == 0)
                    {
                        s.Append(" : ");
                    }
                    else
                    {
                        s.Append(", ");
                    }
                    s.Append(link.GetDestination().GetClassName());
                }
            }

            return s.ToString();
        }

        internal override string GetClassName()
        {
            return name;
        }

        internal override void Dump(TreeNode parent)
        {
            TreeNode classNode = parent.Nodes.Add("Class: " + name + "{" + GetHashCode() + "}");
            TreeNode slotsNode = classNode.Nodes.Add("Slots: ");

            topSlots.Dump(slotsNode);
            leftSlots.Dump(slotsNode);
            bottomSlots.Dump(slotsNode);
            rightSlots.Dump(slotsNode);

            base.Dump(classNode);
        }

        internal void ShallowCopyClassWidget(ClassWidget target)
        {
            target.name = name;
            target.pattern = pattern;

            // Copy the methods and attributes
            target.methods = new List<UmlMethod>();
            target.attributes = new List<UmlDesigner.Model.Attribute>();

            foreach (UmlMethod m in methods)
            {
                UmlMethod method = new UmlMethod();
                m.DeepCopy(method);
                target.methods.Add(method);
            }

            foreach (UmlDesigner.Model.Attribute a in attributes)
            {
                UmlDesigner.Model.Attribute attr = new UmlDesigner.Model.Attribute();
                attr.accessLevel = a.accessLevel;
                attr.name = a.name;
                attr.type = a.type;
                target.attributes.Add(attr);
            }

            ShallowCopyItemWidget(target);
        }

        public string name = null;

        Color classBackgroundColor = Color.FromArgb(181, 161, 212);
        Color classOutlineColor = Color.FromArgb(95, 76, 126);

        float[] pattern = new float[2];

        internal List<UmlMethod> methods = new List<UmlMethod>();
        internal List<UmlDesigner.Model.Attribute> attributes = new List<UmlDesigner.Model.Attribute>();
    }
}
